package com.rt.gptnext.common.bean;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.Data;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author gaoyiyang
 **/
@Data
public class PageResult<T> implements Serializable {

    private int current;

    private int size;

    private int total;

    private int totalPages;

    private List<T> list;

    /**
     * 返回空的分页对象
     *
     * @return 空分页对象
     */
    public static <T> PageResult<T> empty() {
        PageResult<T> result = new PageResult<>();
        result.setCurrent(0);
        result.setSize(0);
        result.setTotal(0);
        result.setTotalPages(0);
        result.setList(Collections.emptyList());
        return result;
    }

    /**
     * 解析分页结果
     *
     * @param page      mybatis-plus分页原始对象
     * @param converter 转换函数
     * @return 转换结果
     */
    public static <T, S> PageResult<T> parse(IPage<S> page, Function<S, T> converter) {
        PageResult<T> result = new PageResult<>();
        result.setCurrent(Math.toIntExact(page.getCurrent()));
        result.setSize(Math.toIntExact(page.getSize()));
        result.setTotal(Math.toIntExact(page.getTotal()));

        int totalPages = 0;
        if (result.getSize() > 0) {
            totalPages = result.getTotal() / result.getSize();
            if (result.getTotal() % result.getSize() > 0) {
                totalPages += 1;
            }
        }
        result.setTotalPages(totalPages);
        if (converter != null) {
            result.setList(page.getRecords().stream().map(converter).collect(Collectors.toList()));
        } else {
            @SuppressWarnings("unchecked")
            List<T> records = (List<T>) page.getRecords();
            result.setList(records);
        }
        return result;
    }

    /**
     * 解析分页结果
     *
     * @param page        mybatis-plus分页原始对象
     * @param targetClass 目标类型
     * @param handler     转换结果处理函数
     * @return 转换结果
     */
    public static <T, S> PageResult<T> parse(IPage<S> page, Class<T> targetClass, Consumer<T> handler) {
        S data = CollUtil.get(page.getRecords(), 0);
        Function<S, T> converter;
        if (targetClass == null || data == null || data.getClass() == targetClass) {
            converter = null;
        } else {
            converter = s -> BeanUtil.copyProperties(s, targetClass);
        }
        PageResult<T> result = parse(page, converter);
        if (handler != null) {
            result.getList().forEach(handler);
        }
        return result;
    }

    /**
     * 异步解析
     *
     * @param page        mybatis-plus分页原始对象
     * @param targetClass 目标类型
     * @param handler     转换结果处理函数
     * @return 转换结果
     */
    public static <T, S> PageResult<T> parseAsync(IPage<S> page, Class<T> targetClass, Consumer<T> handler) {
        S data = CollUtil.get(page.getRecords(), 0);
        Function<S, T> converter;
        if (targetClass == null || data == null || data.getClass() == targetClass) {
            converter = null;
        } else {
            converter = s -> BeanUtil.copyProperties(s, targetClass);
        }
        PageResult<T> result = parse(page, converter);
        if (handler != null && CollUtil.isNotEmpty(result.getList())) {
            CountDownLatch latch = new CountDownLatch(result.getList().size());
            result.getList().forEach(item -> {
                ThreadUtil.execAsync(() -> {
                    try {
                        handler.accept(item);
                    } finally {
                        latch.countDown();
                    }
                });
            });
            try {
                latch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e.getMessage());
            }
        }
        return result;
    }

    public static <T, S> PageResult<T> parse(IPage<S> page, Class<T> targetClass) {
        return parse(page, targetClass, null);
    }

    public static <T> PageResult<T> parse(IPage<T> page) {
        return parse(page, null, null);
    }

    public static <T> PageResult<T> parse(IPage<T> page, Consumer<T> handler) {
        return parse(page, null, handler);
    }

}
